<?php

namespace common\service;


use common\model\EntityModel;
use common\model\ProjectCapacityReportMonthModel;
use common\model\ProjectMemberModel;
use common\model\ProjectModel;
use common\model\StatusModel;
use common\model\TaskMemberModel;
use common\model\UserModel;
use Nyg\Holiday;
use support\EntityTaskFilterParser;
use think\Exception;
use support\ErrorCode;
use support\SingletonTrait;
use common\model\PlanModel;
use common\model\TaskModel;
use common\model\StepCategoryModel;
use common\exception\LogicException;
use common\model\AttributeValueModel;
use common\model\ProjectCapacityReportModel;


class ProjectCapacityReportService
{
    // 单例工具
    use SingletonTrait;

    use EntityTaskFilterParser;

    /**
     * 查询产能显示分类
     * @param $projectIdList
     * @return array
     * @throws Exception
     */
    public function getShowStepCategoryList($projectIdList)
    {
        $projectList = model(ProjectModel::class)
            ->where(['id' => ['IN', $projectIdList]])
            ->field('id,project_template_id')
            ->select();
        $projectTemplateIdList = array_filter(array_column($projectList, 'project_template_id'));
        if (empty($projectTemplateIdList)) {
            throw new LogicException('project template not found', ErrorCode::PROJECT_TEMPLATE_NOT_FOUND);
        }

        $settings = TemplateService::getInstance()->getStatisticalStepTemplateList($projectTemplateIdList);
        $res = [];
        $settings = array_column($settings, null, 'id');

        foreach ($projectList as $project) {
            $setting = $settings[$project['project_template_id']] ?? null;
            if (empty($setting)) {
                continue;
            }
            $currentProjectId = $project['id'];
            $setting['config'] = array_filter($setting['config'], function ($i) {
                return $i['is_show'] === 'yes';
            });
            $group = [];
            foreach ($setting['config'] as $showStepCategory) {
                $showStepCategory['link_module_code'] = 'step_category';
                if (empty($group[$showStepCategory['show_name']])) {
                    $group[$showStepCategory['show_name']] = [
                        "id" => $showStepCategory['show_name'],
                        "name" => $showStepCategory['show_name'],
                        'link_module_code' => 'show_step_category',
                        "children" => [$showStepCategory],
                        "children_ids" => [$showStepCategory['id']]
                    ];
                } else {
                    $group[$showStepCategory['show_name']]['children'][] = $showStepCategory;
                    $group[$showStepCategory['show_name']]['children_ids'][] = $showStepCategory['id'];
                }
            }

            foreach ($group as $name => $groupList) {
                if (!isset($res[$name])) {
                    $groupList['project_id_list'] = [$currentProjectId];
                    $res[$name] = $groupList;
                    continue;
                }
                $res[$name]['project_id_list'][] = $currentProjectId;
            }
        }
        $res = array_values($res);

        $res[] = [
            'id' => TaskService::DIRECTOR_CODE,
            'name' => '导演',
            'children' => [
                [
                    'id' => TaskService::DIRECTOR_CODE,
                    'name' => '导演',
                ],
            ],
            'children_ids' => [
                0
            ],
            'project_id_list' => array_column($projectList, 'id'),
        ];
        return $res;
    }

    /**
     * 验证传进来的分类是不是一个类型下的
     * @param array $moduleIdList
     * @return void
     */
    public function validatePCModuleList(array $moduleIdList)
    {
        $checkArr = [
            'asset' => 0,
            'shot' => 0,
            'other' => 0,
        ];
        //
        foreach ($moduleIdList as $moduleId) {
            $moduleCode = module_by_id($moduleId)['code'];
            if (in_array($moduleCode, ['design', 'asset', 'level'])) {
                $checkArr['asset'] = 1;
            } else if (in_array($moduleCode, ['shot', 'storyboard', 'sequence'])) {
                $checkArr['shot'] = 1;
            } else {
                $checkArr['other'] = 1;
            }
        }

        $exist = array_filter($checkArr, function ($i) {
            return $i > 0;
        });
        if (count($exist) >= 2) {
            throw new LogicException('step_category_id can`t cross module', ErrorCode::DATA_INVALID);
        }
    }

    /**
     * 格式化产能数据
     * @param       $moduleCode
     * @param array $configTemplateList
     * @return array
     */
    public function formatModulePCConfigData($moduleCode, array $configTemplateList)
    {

        $configTemplateList = array_column($configTemplateList, null, 'group_uk');
        $configTemplateList = array_values($configTemplateList);
        $res = [];
        switch ($moduleCode) {
            case 'design':
            case 'asset':
            case 'level':
                $total = 0;
                foreach ($configTemplateList as &$item) {
                    $temp = explode('-', $item['group_uk']);
                    $item['asset_category'] = $temp[0] ?? '';
                    $item['asset_grade'] = $temp[1] ?? '';
                }
                $configTemplateListGrouped = array_group_by($configTemplateList, 'asset_category');
                foreach ($configTemplateListGrouped as $assetCategory => $configTemplateList) {
                    $res[] = [
                        'column_name' => $assetCategory,
                        'column_type' => 'asset',
                        'total' => array_sum(array_column($configTemplateList, 'number')),
                        'rows' => $configTemplateList,
                    ];
                }
                break;
            case 'shot':
                $res[] = [
                    'column_name' => '镜头',
                    'column_type' => 'shot',
                    'rows' => $configTemplateList,
                ];
                break;
            case TaskService::DIRECTOR_CODE:
                $res[] = [
                    'column_name' => '导演',
                    'column_type' => TaskService::DIRECTOR_CODE,
                    'rows' => $configTemplateList,
                ];
                break;
            case 'sequence':
            case 'storyboard':
            default:
                $res[] = [
                    'column_name' => '剧集',
                    'column_type' => 'episode',
                    'rows' => $configTemplateList,
                ];
        }
        return $res;
    }

    /**
     * 获得模块产能 配置模板数据
     * @param $moduleCode
     * @param $stepCategoryIdList
     * @return array
     */
    public function getModulePCConfigTemplate($moduleCode, $stepCategoryIdList)
    {
        $res = [];
        $assetCategoryList = model(AttributeValueModel::class)
            ->where("attribute_id = (select id from attribute where code = 'zichanleixing')")
            ->cache(600)
            ->column('value');
        $assetGradeList = model(AttributeValueModel::class)
            ->where("attribute_id = (select id from attribute where code = 'dengji')")
            ->cache(600)
            ->column('value');

        $jiesuanStepCategoryIdList = model(StepCategoryModel::class)
            ->where(['code' => ['IN', ['solution', 'shot_solution']]])
            ->field('id')
            ->cache(600)
            ->column('id');

        switch ($moduleCode) {
            case 'design':
                foreach ($stepCategoryIdList as $stepCategoryId) {
                    foreach ($assetCategoryList as $assetCategory) {
                        foreach ($assetGradeList as $assetGrade) {
                            $res[] = [
                                'uk' => sprintf("%s-%s-%d", $assetCategory, $assetGrade, $stepCategoryId),
                                'group_uk' => sprintf("%s-%s", $assetCategory, $assetGrade),
                                'step_category_id' => $stepCategoryId,
                                'number_logic' => 'asset_number',
                                'number' => 0
                            ];
                        }
                    }
                }
                break;
            case 'asset':
                foreach ($stepCategoryIdList as $stepCategoryId) {
                    foreach ($assetCategoryList as $assetCategory) {
                        if ($assetCategory == '关卡') {
                            continue;
                        }
                        foreach ($assetGradeList as $assetGrade) {
                            $res[] = [
                                'uk' => sprintf("%s-%s-%d", $assetCategory, $assetGrade, $stepCategoryId),
                                'group_uk' => sprintf("%s-%s", $assetCategory, $assetGrade),
                                'step_category_id' => $stepCategoryId,
                                'number_logic' => 'asset_number',
                                'number' => 0
                            ];
                        }
                    }
                }
                break;
            case 'level':
                foreach ($stepCategoryIdList as $stepCategoryId) {
                    foreach ($assetCategoryList as $assetCategory) {
                        if ($assetCategory != '关卡') {
                            continue;
                        }
                        foreach ($assetGradeList as $assetGrade) {
                            $res[] = [
                                'uk' => sprintf("%s-%s-%d", $assetCategory, $assetGrade, $stepCategoryId),
                                'group_uk' => sprintf("%s-%s", $assetCategory, $assetGrade),
                                'step_category_id' => $stepCategoryId,
                                'number_logic' => 'asset_number',
                                'number' => 0
                            ];
                        }
                    }
                }
                break;
            case 'sequence':
            case 'shot':
                foreach ($stepCategoryIdList as $stepCategoryId) {
                    $res[] = [
                        'uk' => sprintf("%s-%d", 'shot_number', $stepCategoryId),
                        'group_uk' => sprintf("%s", 'shot_number'),
                        'step_category_id' => $stepCategoryId,
                        'number_logic' => 'shot_number',
                        'number' => 0
                    ];
                    // 解算比例上限配置
//                    if (in_array($stepCategoryId, $jiesuanStepCategoryIdList)) {
//                        $res[] = [
//                            'uk' => sprintf("%s-%d", 'high_solution_limit', $stepCategoryId),
//                            'group_uk' => sprintf("%s", 'high_solution_limit'),
//                            'step_category_id' => $stepCategoryId,
//                            'number_logic' => 'high_solution_limit',
//                            'number' => 0
//                        ];
//                    }
                }
                break;
            case TaskService::DIRECTOR_CODE:
                $res[] = [
                    'uk' => sprintf("%s-%s-%d", TaskService::DIRECTOR_CODE, 'meeting_number', 0),
                    'group_uk' => sprintf("%s-%s", TaskService::DIRECTOR_CODE, 'meeting_number'),
                    'step_category_id' => 0,
                    'number_logic' => 'meeting_number',
                    'number' => 0
                ];
                $res[] = [
                    'uk' => sprintf("%s-%s-%d", TaskService::DIRECTOR_CODE, 'review_number', 0),
                    'group_uk' => sprintf("%s-%s", TaskService::DIRECTOR_CODE, 'review_number'),
                    'step_category_id' => 0,
                    'number_logic' => 'review_number',
                    'number' => 0
                ];
                break;
            case 'storyboard':
            default:
                foreach ($stepCategoryIdList as $stepCategoryId) {
                    $res[] = [
                        'uk' => sprintf("%s-%d", 'episode_number', $stepCategoryId),
                        'group_uk' => sprintf("%s", 'episode_number'),
                        'step_category_id' => $stepCategoryId,
                        'number_logic' => 'episode',
                        'number' => 0
                    ];
                }
        }
        return $res;
    }

    /**
     * @param int   $projectId
     * @param array $stepCategoryIdList
     * @param       $year
     * @return array
     * @throws Exception
     */
    public function selectByStepCategory(int $projectId, array $stepCategoryIdList, $year)
    {
        $ys = strtotime($year . '-01-01 00:00:00');
        $ye = strtotime($year . '-12-01 00:00:00');
        $filter = [
            [
                'report_month' => ['EGT', $ys]
            ],
            [
                'report_month' => ['ELT', $ye]
            ],
            'project_id' => $projectId,
            'step_category_id' => [
                'IN', $stepCategoryIdList,
            ]
        ];

        $stepCategoryIds = implode(',', $stepCategoryIdList);
        $topLevelStepCategoryList = model(StepCategoryModel::class)
            ->where("id in (select parent_id from step_category where id in ({$stepCategoryIds}))")
            ->select();
        if (!empty($topLevelStepCategoryList)) {
            $moduleIdList = array_column($topLevelStepCategoryList, 'entity_id');
            $this->validatePCModuleList($moduleIdList);
            $moduleCode = module_by_id($moduleIdList[0])['code'];
        } else {
            $moduleCode = TaskService::DIRECTOR_CODE;
        }

        $defaultConfig = ProjectService::getInstance()->getPCConfig($projectId, $stepCategoryIdList, [], false);

        $rows = model(ProjectCapacityReportModel::class)->where($filter)->field("*,from_unixtime(report_month,'%Y-%m') as report_month")->select();
        $rowsGrouped = array_group_by($rows, 'report_month');
        $res = [];
        for ($i = 1; $i <= 12; $i += 1) {
            $yearMonth = $year . '-' . str_pad($i, 2, 0, STR_PAD_LEFT);
            $data = $rowsGrouped[$yearMonth] ?? [];
            $data = array_column($data, 'number', 'group_uk');
            $currentConfig = [];
            $filled = false;
            foreach ($defaultConfig as $item) {
                if (isset($data[$item['group_uk']])) {
                    $item['number'] = $data[$item['group_uk']];
                    $filled = true;
                }
                $currentConfig[] = $item;
            }
            $res[] = [
                'year_month' => $yearMonth,
                'filled' => $filled,
                'rows' => $this->formatModulePCConfigData($moduleCode, $currentConfig),
            ];
        }
        return $res;
    }

    /**
     * 保存配置
     * @param int   $projectId
     * @param array $stepCategoryIdList
     * @param int   $yearMonthTimeStamp
     * @param       $configData
     * @return array
     * @throws Exception
     */
    public function saveByStepCategory(int $projectId, array $stepCategoryIdList, int $yearMonthTimeStamp, $configData)
    {
        $filter = [
            'report_month' => $yearMonthTimeStamp,
            'project_id' => $projectId,
            'step_category_id' => [
                'IN', $stepCategoryIdList,
            ],
        ];
        $stepCategoryIds = implode(',', $stepCategoryIdList);
        $topLevelStepCategoryList = model(StepCategoryModel::class)
            ->where("id in (select parent_id from step_category where id in ({$stepCategoryIds}))")
            ->select();

        if (!empty($topLevelStepCategoryList)) {
            $moduleIdList = array_column($topLevelStepCategoryList, 'entity_id');
            ProjectCapacityReportService::getInstance()->validatePCModuleList($moduleIdList);
            $moduleCode = module_by_id($moduleIdList[0])['code'];
        } else {
            $moduleCode = TaskService::DIRECTOR_CODE;
        }


        $configTemplateList = $this->getModulePCConfigTemplate($moduleCode, $stepCategoryIdList);

        $existList = model(ProjectCapacityReportModel::class)->where($filter)->select();
        $existList = array_column($existList, null, 'uk');


        $existListMapByGroupUk = array_column($existList, 'number', 'group_uk');
        $configNumberMapByUk = array_column($configData, 'number', 'uk');

        $update = [];
        $create = [];
        foreach ($configTemplateList as $configTemplate) {
            $uk = $configTemplate['uk'];
            $groupUk = $configTemplate['group_uk'];
            if (isset($configNumberMapByUk[$uk])) {
                $number = $configNumberMapByUk[$uk];
            } elseif (isset($existListMapByGroupUk[$groupUk])) {
                $number = $existListMapByGroupUk[$groupUk];
            } else {
                $number = 0;
            }

            if (isset($existList[$uk])) {
                if ($existList[$uk]['number'] != $number) {
                    model(ProjectCapacityReportModel::class)->modifyItem(['id' => $existList[$uk]['id'], 'number' => $number]);
                }
            } else {
                $configTemplate['number'] = $number;
                $configTemplate['tenant_id'] = auto_fill_tenant_id();
                $configTemplate['project_id'] = $projectId;
                $configTemplate['created_by'] = fill_created_by();
                $configTemplate['created'] = time();
                $configTemplate['report_month'] = $yearMonthTimeStamp;
                $create[] = $configTemplate;
            }
        }
        if (!empty($create)) {
            model(ProjectCapacityReportModel::class)->addAll($create);
        }

        return $this->selectByStepCategory($projectId, $stepCategoryIdList, date("Y", $yearMonthTimeStamp));
    }

    /**
     * 产能折线图
     * @param        $projectIdList
     * @param array  $stepCategoryIdList
     * @param int    $startDate
     * @param int    $endDate
     * @param string $categoryName
     * @param int    $directorUserId
     * @return array
     * @throws Exception
     */
    public function selectPlanRealData($projectIdList, array $stepCategoryIdList, int $startDate, int $endDate, string $categoryName, int $directorUserId = 0)
    {
        $planData = $this->selectPlanData($projectIdList, $stepCategoryIdList, $startDate, $endDate, $categoryName, $directorUserId);
        $realData = $this->selectReportData($projectIdList, $stepCategoryIdList, $startDate, $endDate, $categoryName, $directorUserId);
        return ['plan' => array_values($planData), 'report' => array_values($realData)];
    }

    /**
     * 查询排期数据
     * @param       $projectIdList
     * @param array $stepCategoryIdList
     * @param int   $startDate
     * @param int   $endDate
     * @param       $categoryName
     * @param       $directorUserId
     * @return array
     */
    private function selectPlanData($projectIdList, array $stepCategoryIdList, int $startDate, int $endDate, $categoryName, $directorUserId)
    {
        $stepCategoryIds = implode(',', $stepCategoryIdList);
        $topLevelStepCategoryList = model(StepCategoryModel::class)
            ->where("id in (select parent_id from step_category where id in ({$stepCategoryIds}))")
            ->select();

        if (!empty($topLevelStepCategoryList)) {
            $moduleIdList = array_column($topLevelStepCategoryList, 'entity_id');
            ProjectCapacityReportService::getInstance()->validatePCModuleList($moduleIdList);
            $moduleCode = module_by_id($moduleIdList[0])['code'];
        } else {
            $moduleCode = TaskService::DIRECTOR_CODE;
        }

        $res = [];

        $startMonth = date('Y-m', $startDate);
        $endMonth = date('Y-m', $endDate);
        $currentMonth = $startMonth;
        do {
            $res[$currentMonth] = 0;
            $currentMonth = date("Y-m", strtotime($currentMonth . ' +1 month'));
        } while (strtotime($currentMonth) <= strtotime($endMonth));
        // 统计环节排产的 剧集
        $pFilter = [
            'p.end_time' => ['BETWEEN', [$startDate, $endDate]],
            'p.project_id' => ['IN', $projectIdList],
            'p.type' => PlanModel::TYPE_STEP_CATEGORY,
            'p.scope' => PlanModel::SCOPE_SINGLE,
            'trt.link_id' => ['IN', $stepCategoryIdList],
        ];
        $episodeStepCategoryPlanList = model(PlanModel::class)->alias('p')
            ->join('task on task.id = p.task_id')
            ->join("(select * from task_relation_tag where link_module_code in ('step_category', 'show_step_category')) trt on trt.task_id = task.id")
            ->field('p.start_time,p.end_time,p.entity_id as episode_id,trt.link_id as step_category_id')
            ->where($pFilter)
            ->group('p.entity_id,trt.link_id')
            ->select();
        $taskCountEpisodeStepCategoryFilter = [];
        foreach ($episodeStepCategoryPlanList as &$episodeStepCategoryPlanItem) {
            $episodeStepCategoryPlanItem['end_month'] = date("Y-m", $episodeStepCategoryPlanItem['end_time']);
            $episodeStepCategoryPlanItem['date_percent'] = $this->getMonthPercent($episodeStepCategoryPlanItem['start_time'], $episodeStepCategoryPlanItem['end_time']);
            $taskCountEpisodeStepCategoryFilter[] = [
                'task.episode_id' => $episodeStepCategoryPlanItem['episode_id'],
                'task.step_category_id' => $episodeStepCategoryPlanItem['step_category_id'],
            ];
            $episodeStepCategoryPlanItem['unique_key'] = sprintf("%d-%d", $episodeStepCategoryPlanItem['episode_id'], $episodeStepCategoryPlanItem['step_category_id']);
        }
        $episodeStepCategoryPlanList = array_group_by($episodeStepCategoryPlanList, 'end_month');
        switch ($moduleCode) {
            case 'design':
            case 'asset':
            case 'level':
                if (empty($taskCountEpisodeStepCategoryFilter)) {
                    return $res;
                }
                $taskCountEpisodeStepCategoryFilter['_logic'] = 'or';
                $taskCountFilter = [
                    $taskCountEpisodeStepCategoryFilter,
                    'task.step_type' => 'step_category',
                    'task.project_id' => ['IN', $projectIdList],
                ];
                if (!empty($categoryName)) {
                    $taskCountFilter["JSON_UNQUOTE(JSON_EXTRACT(entity.json,concat('$.',module.code,'_category')))"] = $categoryName;
                }
                $this->queryMonthPlanTaskCountData($res, $taskCountFilter, $episodeStepCategoryPlanList);
                break;
            case 'shot':
                if (empty($taskCountEpisodeStepCategoryFilter)) {
                    return $res;
                }
                $taskCountEpisodeStepCategoryFilter['_logic'] = 'or';
                $taskCountFilter = [
                    $taskCountEpisodeStepCategoryFilter,
                    'task.step_type' => 'step_category',
                    'task.project_id' => ['IN', $projectIdList],
                ];
                $this->queryMonthPlanTaskCountData($res, $taskCountFilter, $episodeStepCategoryPlanList);
                break;
            case TaskService::DIRECTOR_CODE:
                $roleId = teamones_request('teamones-im', 'role/find', ['param' => ['filter' => ['code' => TaskService::DIRECTOR_CODE]]]);
                if (empty($roleId['data'])) {
                    return $res;
                }
                $dqFilter = [
                    "project_id" => ['IN', $projectIdList],
                    'role_id' => $roleId['data']['id']
                ];
                if ($directorUserId > 0) {
                    $dqFilter['user_id'] = $directorUserId;
                }
                $directorUserIdList = model(ProjectMemberModel::class)
                    ->where($dqFilter)
                    ->group("user_id")
                    ->field("user_id")
                    ->column("user_id");
                if (empty($directorUserIdList)) {
                    return $res;
                }
                $directorPlanFilter = [
                    'p.project_id' => ['IN', $projectIdList],
                    'p.type' => ['IN', [PlanModel::TYPE_MEETING, PlanModel::TYPE_REVIEW]],
                    'tm.partner' => ['IN', implode(',', $directorUserIdList)],
                    ['p.end_time' => ['ELT', $endDate]],
                    ['p.end_time' => ['EGT', $startDate]],
                ];
                $directorPlanCount = model(PlanModel::class)
                    ->alias('p')
                    ->join('task_member tm on tm.task_id = p.task_id')
                    ->where($directorPlanFilter)
                    ->field("from_unixtime(p.end_time, '%Y-%m') as month, count(distinct p.id) as count")
                    ->group("from_unixtime(p.end_time, '%Y-%m')")
                    ->select();
                $directorPlanCount = array_column($directorPlanCount, 'count', 'month');
                foreach ($directorPlanCount as $month => $count) {
                    $res[$month] = $count;
                }
                break;
            case 'episode':
            case 'sequence':
            case 'storyboard':
            default:
                $episodeStepCategoryPlanFilter = [
                    'p.project_id' => ['IN', $projectIdList],
                    'p.type' => ['IN', [PlanModel::TYPE_STEP_CATEGORY]],
                    'p.scope' => PlanModel::SCOPE_SINGLE,
                    ['p.end_time' => ['ELT', $endDate]],
                    ['p.end_time' => ['EGT', $startDate]],
                    'r_step_category.link_id' => ['IN', $stepCategoryIdList],
                ];
                $directorPlanCount = model(PlanModel::class)
                    ->alias('p')
                    ->join("(select * from task_relation_tag where link_module_code = 'episode') r_episode on r_episode.task_id = p.task_id")
                    ->join("(select * from task_relation_tag where link_module_code in ('step_category', 'show_step_category')) r_step_category on r_step_category.task_id = p.task_id")
                    ->where($episodeStepCategoryPlanFilter)
                    ->field("from_unixtime(p.end_time, '%Y-%m') as month, count(distinct r_episode.link_id) as count")
                    ->group("from_unixtime(p.end_time, '%Y-%m')")
                    ->select();
                $directorPlanCount = array_column($directorPlanCount, 'count', 'month');
                foreach ($directorPlanCount as $month => $count) {
                    $res[$month] = $count;
                }
        }
        return $res;


    }


    /**
     * 根据开始、结束时间：计算开始、结束时间占当月总天数的百分比
     * @param $startTime
     * @param $endTime
     * @return array
     */
    public function getMonthPercent($startTime, $endTime)
    {
        $startMonth = date('Y-m', $startTime);  //开始日期 年-月
        $endMonth = date('Y-m', $endTime);    //结束日期 年-月
        $startDay = date('d', $startTime);      //开始日期 几号
        $endDay = date('d', $endTime);        //结束日期 几号


        $diff_seconds = $endTime - $startTime;
        $time = floor(($diff_seconds) / 86400);
        $totalDays = $time + 1;
        $dateToPercent = [];
        $currentMonth = $startMonth;
        $t = 0;
        do {
            $currentLastDay = date('t', strtotime($currentMonth)); //开始月份的最后一天（开始月份总天数）
            $dayLength = 0;
            if ($startMonth == $endMonth) {
                $dayLength = $endDay - $startDay;
            } else {
                if ($currentMonth == $startMonth) {
                    $dayLength = $currentLastDay - $startDay;
                } elseif ($currentMonth == $endMonth) {
                    $dayLength = $endDay;
                } else {
                    $dayLength = $currentLastDay;
                }
            }

            $dateToPercent[$currentMonth] = $dayLength / $totalDays;
            $t += $dateToPercent[$currentMonth];
            $currentMonth = date("Y-m", strtotime($currentMonth . ' + 1 month'));
        } while (strtotime($currentMonth) <= strtotime($endMonth));
        return $dateToPercent;
    }

    /**
     * 等级转换规则
     * @param $moduleCode
     * @param $stepCategoryIdList
     * @return array
     */
    public function getModulePCLevelConfigTemplate($moduleCode, $stepCategoryIdList)
    {
        $res = [];
        $assetCategoryList = model(AttributeValueModel::class)
            ->where("attribute_id = (select id from attribute where code = 'zichanleixing')")
            ->cache(600)
            ->column('value');
        $assetGradeList = model(AttributeValueModel::class)
            ->where("attribute_id = (select id from attribute where code = 'dengji')")
            ->order("index ASC")
            ->cache(600)
            ->column('value');

        $gradeTotal = count($assetGradeList);
        switch ($moduleCode) {
            case 'design':
                foreach ($stepCategoryIdList as $stepCategoryId) {
                    foreach ($assetCategoryList as $assetCategory) {
                        foreach ($assetGradeList as $gradeIndex => $assetGrade) {
                            if ($gradeIndex == $gradeTotal - 1) {
                                continue;
                            }
                            $nextGrade = $assetGradeList[$gradeIndex + 1];
                            $res[] = [
                                'uk' => sprintf("%s-%s-%s-%d", $assetCategory, $assetGrade, $nextGrade, $stepCategoryId),
                                'group_uk' => sprintf("%s-%s-%s", $assetCategory, $assetGrade, $nextGrade),
                                'step_category_id' => $stepCategoryId,
                                'asset_grade' => $assetGrade,
                                'number_logic' => 'low_to_' . $nextGrade,
                                'to_grade' => $nextGrade,
                                'number' => 1
                            ];
                        }
                    }
                }
                break;
            case 'asset':
                foreach ($stepCategoryIdList as $stepCategoryId) {
                    foreach ($assetCategoryList as $assetCategory) {
                        if ($assetCategory == '关卡') {
                            continue;
                        }
                        foreach ($assetGradeList as $gradeIndex => $assetGrade) {
                            if ($gradeIndex == $gradeTotal - 1) {
                                continue;
                            }
                            $nextGrade = $assetGradeList[$gradeIndex += 1];
                            $res[] = [
                                'uk' => sprintf("%s-%s-%s-%d", $assetCategory, $assetGrade, $nextGrade, $stepCategoryId),
                                'group_uk' => sprintf("%s-%s-%s", $assetCategory, $assetGrade, $nextGrade),
                                'step_category_id' => $stepCategoryId,
                                'number_logic' => 'low_to_' . $nextGrade,
                                'to_grade' => $nextGrade,
                                'number' => 1
                            ];
                        }
                    }
                }
                break;
            case 'level':
                foreach ($stepCategoryIdList as $stepCategoryId) {
                    foreach ($assetCategoryList as $assetCategory) {
                        if ($assetCategory != '关卡') {
                            continue;
                        }
                        foreach ($assetGradeList as $gradeIndex => $assetGrade) {
                            if ($gradeIndex == $gradeTotal - 1) {
                                continue;
                            }
                            $nextGrade = $assetGradeList[$gradeIndex += 1];
                            $res[] = [
                                'uk' => sprintf("%s-%s-%s-%d", $assetCategory, $assetGrade, $nextGrade, $stepCategoryId),
                                'group_uk' => sprintf("%s-%s-%s", $assetCategory, $assetGrade, $nextGrade),
                                'step_category_id' => $stepCategoryId,
                                'number_logic' => 'low_to_' . $nextGrade,
                                'to_grade' => $nextGrade,
                                'number' => 1
                            ];
                        }
                    }
                }
                break;
            case 'sequence':
            case 'storyboard':
            case 'shot':
            default:
        }
        return $res;
    }

    /**
     * 查询上报的数据
     * @param        $projectIdList
     * @param array  $stepCategoryIdList
     * @param int    $startDate
     * @param int    $endDate
     * @param string $categoryName
     * @param        $directorUserId
     * @return array
     * @throws Exception
     */
    private function selectReportData($projectIdList, array $stepCategoryIdList, int $startDate, int $endDate, string $categoryName, $directorUserId)
    {
        $projectsPcTemplate = ProjectService::getInstance()->getMultiPCConfig($projectIdList, $stepCategoryIdList);

        $moduleCode = $this->getStepCategoryListModuleCode($stepCategoryIdList);
        $res = [];

        $startMonth = date('Y-m', $startDate);
        $endMonth = date('Y-m', $endDate);
        $currentMonth = $startMonth;
        do {
            $res[$currentMonth] = 0;
            $currentMonth = date("Y-m", strtotime($currentMonth . ' +1 month'));
        } while (strtotime($currentMonth) <= strtotime($endMonth));

        $assetGradeList = model(AttributeValueModel::class)
            ->where("attribute_id = (select id from attribute where code = 'dengji')")
            ->order("index ASC")
            ->cache(600)
            ->column('value');
        $pFilter = [
            ['p.report_month' => ['ELT', $endDate],],
            ['p.report_month' => ['EGT', $startDate],],
            'p.project_id' => ['IN', $projectIdList],
            'p.step_category_id' => ['IN', $stepCategoryIdList],
        ];
        if (in_array($moduleCode, ['design', 'asset', 'level']) && !empty($categoryName)) {
            $pFilter[] = ['p.group_uk' => ['LIKE', $categoryName . '-%']];
        }
        $episodeStepCategoryPlanList = model(ProjectCapacityReportModel::class)->alias('p')
            ->where($pFilter)
            ->field("*,from_unixtime(report_month,'%Y-%m') as month")
            ->select();
        $episodeStepCategoryPlanList = array_group_by($episodeStepCategoryPlanList, 'project_id', 'month');
        switch ($moduleCode) {
            case 'design':
            case 'asset':
            case 'level':
                foreach ($projectsPcTemplate as &$pcTemplate) {
                    $pcTemplate = array_filter($pcTemplate, function ($i) use ($categoryName) {
                        return strstr($i['group_uk'], $categoryName) !== false;
                    });
                    $pcTemplate = array_column($pcTemplate, 'number', 'group_uk');
                }
                unset($pcTemplate);
                foreach ($projectIdList as $projectId) {
                    $currentProjectReportData = $episodeStepCategoryPlanList[$projectId] ?? [];
                    $pcTemplate = $projectsPcTemplate[$projectId] ?? [];
                    foreach ($res as $month => &$count) {
                        $currentMonthProjectReportData = $currentProjectReportData[$month] ?? [];
                        $currentMonthProjectReportData = array_column($currentMonthProjectReportData, 'number', 'group_uk');
                        $totalNumber = 0;
                        foreach ($assetGradeList as $grade) {
                            $groupUk = $categoryName . '-' . $grade;
                            $number = $currentMonthProjectReportData[$groupUk] ?? ($pcTemplate[$groupUk] ?? 0);
                            $totalNumber += $number;
                        }
                        $count += $totalNumber;
                    }
                }
                break;
            case 'shot':
                foreach ($projectIdList as $projectId) {
                    $pcTemplate = $projectsPcTemplate[$projectId] ?? [];
                    $pcTemplate = $pcTemplate[0];
                    $projectEpisodeStepCategoryPlanList = $episodeStepCategoryPlanList[$projectId] ?? [];
                    $projectEpisodeStepCategoryPlanList = array_column($projectEpisodeStepCategoryPlanList, 'number', 'month');
                    foreach ($res as $month => &$count) {
                        if (isset($projectEpisodeStepCategoryPlanList[$month])) {
                            $number = $projectEpisodeStepCategoryPlanList[$month];
                        } elseif (!empty($pcTemplate)) {
                            $number = $pcTemplate['number'];
                        } else {
                            $number = 0;
                        }
                        $count = $number;
                    }
                }

                break;
            case TaskService::DIRECTOR_CODE:
                $roleId = teamones_request('teamones-im', 'role/find', ['param' => ['filter' => ['code' => TaskService::DIRECTOR_CODE]]]);
                if (empty($roleId['data'])) {
                    return $res;
                }
                $dqFilter = [
                    "project_id" => ['IN', $projectIdList],
                    'role_id' => $roleId['data']['id']
                ];
                if ($directorUserId > 0) {
                    $dqFilter['user_id'] = $directorUserId;
                }
                $projectDirectorUserIdList = model(ProjectMemberModel::class)
                    ->where($dqFilter)
                    ->group("project_id,user_id")
                    ->field("project_id,user_id")
                    ->select();
                if (empty($projectDirectorUserIdList)) {
                    return $res;
                }
                $projectDirectorUserIdList = array_group_by($projectDirectorUserIdList, 'project_id');
                foreach ($projectIdList as $projectId) {
                    $directorUserIdList = $projectDirectorUserIdList[$projectId] ?? [];
                    $directorNumber = count($directorUserIdList);
                    $pcTemplate = $projectsPcTemplate[$projectId] ?? [];
                    $pcTemplate = array_column($pcTemplate, 'number', 'number_logic');
                    $projectEpisodeStepCategoryPlanList = $episodeStepCategoryPlanList[$projectId] ?? [];
                    foreach ($res as $month => &$count) {
                        $currentReportData = $projectEpisodeStepCategoryPlanList[$month] ?? [];
                        $currentReportData = array_column($currentReportData, 'number', 'number_logic');
                        foreach (['review_number', 'meeting_number'] as $numberLogic) {
                            if (isset($currentReportData[$numberLogic])) {
                                $number = $currentReportData[$numberLogic];
                            } elseif (isset($pcTemplate[$numberLogic])) {
                                $number = $pcTemplate[$numberLogic];
                            } else {
                                $number = 0;
                            }
                            $count += $number * $directorNumber;
                        }
                    }
                }

                break;
            case 'sequence':
            case 'storyboard':
            case 'episode':
            default:
                foreach ($projectIdList as $projectId) {
                    $pcTemplate = $projectsPcTemplate[$projectId] ?? [];
                    $pcTemplate = $pcTemplate[0];
                    $projectEpisodeStepCategoryPlanList = $episodeStepCategoryPlanList[$projectId] ?? [];
                    foreach ($res as $month => &$count) {
                        $currentMonthEpisodeStepCategoryPlanList = $projectEpisodeStepCategoryPlanList[$month] ?? [];
                        $currentMonthEpisodeStepCategoryPlanList = array_column($currentMonthEpisodeStepCategoryPlanList, 'number', 'month');
                        if (isset($currentMonthEpisodeStepCategoryPlanList[$month])) {
                            $number = $currentMonthEpisodeStepCategoryPlanList[$month];
                        } elseif (!empty($pcTemplate)) {
                            $number = $pcTemplate['number'];
                        } else {
                            $number = 0;
                        }
                        $count += $number;
                    }

                }
        }
        return $res;
    }

    /**
     * 产能月度详情统计
     * @param        $projectIdList
     * @param array  $stepCategoryIdList
     * @param string $month
     * @param string $categoryName
     * @param int    $directorUserId
     * @param bool   $today
     * @return array|array[]
     * @throws Exception
     */
    public function monthReportCount($projectIdList, array $stepCategoryIdList, string $month, string $categoryName, int $directorUserId, bool $today)
    {
        $res = [
            'plan' => [],
            'report' => [],
        ];

        $startDate = strtotime($month . '-01 00:00:00');
        $endDate = strtotime(" +1month-1seconds", $startDate);

        $projectsPcTemplateList = ProjectService::getInstance()->getMultiPCConfig($projectIdList, $stepCategoryIdList);
        $projectsRealReportList = $this->getMonthReport($projectIdList, $month, $stepCategoryIdList[0]);

        $projectsRealReportList = array_group_by($projectsRealReportList, 'project_id');
        $moduleCode = $this->getStepCategoryListModuleCode($stepCategoryIdList);
        switch ($moduleCode) {
            case 'design':
            case 'asset':
            case 'level':
                $pFilter = [
                    'p.end_time' => ['BETWEEN', [$startDate, $endDate]],
                    'p.project_id' => ['IN', $projectIdList],
                    'p.type' => PlanModel::TYPE_STEP_CATEGORY,
                    'p.scope' => PlanModel::SCOPE_SINGLE,
                    'trt.link_id' => ['IN', $stepCategoryIdList],
                ];
                $episodeStepCategoryPlanList = model(PlanModel::class)->alias('p')
                    ->join('task on task.id = p.task_id')
                    ->join("(select * from task_relation_tag where link_module_code in ('step_category', 'show_step_category')) trt on trt.task_id = task.id")
                    ->field('p.start_time,p.end_time,p.entity_id as episode_id,trt.link_id as step_category_id')
                    ->where($pFilter)
                    ->group('p.entity_id,trt.link_id')
                    ->select();
                $taskCountEpisodeStepCategoryFilter = [];
                foreach ($episodeStepCategoryPlanList as $episodeStepCategoryPlanItem) {
                    $taskCountEpisodeStepCategoryFilter[] = [
                        'task.episode_id' => $episodeStepCategoryPlanItem['episode_id'],
                        'task.step_category_id' => $episodeStepCategoryPlanItem['step_category_id'],
                    ];
                }
                $assetGradeList = model(AttributeValueModel::class)
                    ->where("attribute_id = (select id from attribute where code = 'dengji')")
                    ->order("index ASC")
                    ->cache(600)
                    ->column('value');
                foreach ($projectsPcTemplateList as &$pcTemplateList) {
                    $pcTemplateList = array_filter($pcTemplateList, function ($i) use ($categoryName) {
                        return strstr($i['group_uk'], $categoryName) !== false;
                    });
                    $pcTemplateList = array_column($pcTemplateList, 'number', 'group_uk');
                }

                foreach ($projectsRealReportList as &$realReportList) {
                    $realReportList = array_filter($realReportList, function ($i) use ($categoryName) {
                        return strstr($i['group_uk'], $categoryName) !== false;
                    });
                    $realReportList = array_column($realReportList, 'number', 'group_uk');
                }

                foreach ($assetGradeList as $grade) {
                    $gk = $categoryName . '-' . $grade;
                    $totalReport = 0;
                    foreach ($projectIdList as $projectId) {
                        $realReport = $projectsRealReportList[$projectId] ?? [];
                        $pcTemplate = $projectsPcTemplateList[$projectId] ?? [];
                        $totalReport += $realReport[$gk] ?? $pcTemplate[$gk] ?? 0;
                    }
                    $res['plan'][] = [
                        'grade' => $grade,
                        'number_logic' => 'task',
                        'number' => 0,
                    ];
                    $res['report'][] = [
                        'grade' => $grade,
                        'number_logic' => 'task',
                        'number' => $totalReport,
                    ];
                }

                if (empty($taskCountEpisodeStepCategoryFilter)) {
                    return $res;
                }
                $taskCountEpisodeStepCategoryFilter['_logic'] = 'or';
                $taskCountFilter = [
                    $taskCountEpisodeStepCategoryFilter,
                    'task.step_type' => 'step_category',
                    'task.project_id' => ['IN', $projectIdList],
                    "JSON_UNQUOTE(JSON_EXTRACT(entity.json,concat('$.',module.code,'_category')))" => $categoryName,
                ];

                $episodeStepCategoryTaskCount = model(TaskModel::class)
                    ->join('entity on entity.id = task.entity_id')
                    ->join('module on module.id = entity.module_id')
                    ->where($taskCountFilter)
                    ->field("COUNT(DISTINCT task.entity_id) as count,
                      JSON_UNQUOTE(JSON_EXTRACT(entity.json, concat('$.', module.code, '_grade'))) as grade")
                    ->group("JSON_EXTRACT(entity.json, concat('$.', module.code, '_grade'))")
                    ->select();
                $episodeStepCategoryTaskCount = array_column($episodeStepCategoryTaskCount, 'count', 'grade');

                foreach ($res['plan'] as &$planItem) {
                    $gk = $planItem['grade'];
                    if (isset($episodeStepCategoryTaskCount[$gk])) {
                        $planItem['number'] = $episodeStepCategoryTaskCount[$gk];
                    }
                }

                break;
            case 'shot':
                $pFilter = [
                    'p.end_time' => ['BETWEEN', [$startDate, $endDate]],
                    'p.project_id' => ['IN', $projectIdList],
                    'p.type' => PlanModel::TYPE_STEP_CATEGORY,
                    'p.scope' => PlanModel::SCOPE_SINGLE,
                    'trt.link_id' => ['IN', $stepCategoryIdList],
                ];
                $episodeStepCategoryPlanList = model(PlanModel::class)->alias('p')
                    ->join('task on task.id = p.task_id')
                    ->join("(select * from task_relation_tag where link_module_code in ('step_category', 'show_step_category')) trt on trt.task_id = task.id")
                    ->field('p.start_time,p.end_time,p.entity_id as episode_id,trt.link_id as step_category_id')
                    ->where($pFilter)
                    ->group('p.entity_id,trt.link_id')
                    ->select();
                $taskCountEpisodeStepCategoryFilter = [];
                foreach ($episodeStepCategoryPlanList as $episodeStepCategoryPlanItem) {
                    $taskCountEpisodeStepCategoryFilter[] = [
                        'task.episode_id' => $episodeStepCategoryPlanItem['episode_id'],
                        'task.step_category_id' => $episodeStepCategoryPlanItem['step_category_id'],
                    ];
                }


                if (empty($taskCountEpisodeStepCategoryFilter)) {
                    return $res;
                }
                $taskCountEpisodeStepCategoryFilter['_logic'] = 'or';
                $taskCountFilter = [
                    $taskCountEpisodeStepCategoryFilter,
                    'task.step_type' => 'step_category',
                    'task.project_id' => ['IN', $projectIdList],
                ];

                $episodeStepCategoryTaskCount = model(TaskModel::class)
                    ->join('entity on entity.id = task.entity_id')
                    ->join('module on module.id = entity.module_id')
                    ->where($taskCountFilter)
                    ->count("DISTINCT task.entity_id");

                $rsn = 0;
                foreach ($projectIdList as $projectId) {
                    $pcTemplate = $projectsPcTemplateList[$projectId] ?? [];
                    $realReport = $projectsRealReportList[$projectId] ?? [];
                    $pcTemplate = array_column($pcTemplate, 'number', 'number_logic');
                    $realReport = array_column($realReport, 'number', 'number_logic');
                    $rsn += $realReport['shot_number'] ?? $pcTemplate['shot_number'] ?? 0;
                }
                $res['report'][] = [
                    'number_logic' => 'shot_number',
                    'number' => $rsn,
                ];
                $res['plan'][] = [
                    'number_logic' => 'shot_number',
                    'number' => $episodeStepCategoryTaskCount,
                ];

                break;
            case TaskService::DIRECTOR_CODE:
                foreach (['review_number', 'meeting_number'] as $k) {
                    $res['report'][$k] = 0;
                    $res['plan'][$k] = 0;
                }

                $roleId = teamones_request('teamones-im', 'role/find', ['param' => ['filter' => ['code' => TaskService::DIRECTOR_CODE]]]);
                if (empty($roleId['data'])) {
                    return $res;
                }
                $dqFilter = [
                    "project_id" => ['IN', $projectIdList],
                    'role_id' => $roleId['data']['id']
                ];
                if ($directorUserId > 0) {
                    $dqFilter['user_id'] = $directorUserId;
                }
                $projectsDirectorUserIdList = model(ProjectMemberModel::class)
                    ->where($dqFilter)
                    ->group("project_id,user_id")
                    ->field("project_id,user_id")
                    ->select();
                if (empty($projectsDirectorUserIdList)) {
                    return $res;
                }
                $totalDirectorUserIdList = array_values(array_unique(array_column($projectsDirectorUserIdList, 'user_id')));
                $projectsDirectorUserIdList = array_group_by($projectsDirectorUserIdList, 'project_id');

                foreach ($projectsPcTemplateList as &$projectPcTemplate) {
                    $projectPcTemplate = array_column($projectPcTemplate, 'number', 'number_logic');
                }
                $planFilter = [
                    'plan.project_id' => ['IN', $projectIdList],
                    'plan.type' => ['IN', [PlanModel::TYPE_MEETING, PlanModel::TYPE_REVIEW]],
                    'task_member.partner' => ['IN', $totalDirectorUserIdList]
                ];
                $rFilter = [
                    'project_id' => ['IN', $projectIdList],
                    'step_category_id' => 0,
                ];
                if (!empty($month)) {
                    $startDate = strtotime($month . '-01 00:00:00');
                    $rFilter[] = ['report_month' => $startDate];
                    $planFilter[] = ['plan.end_time' => ['BETWEEN', [strtotime($month . '-01 00:00:00'), strtotime(" +1month-1seconds", $startDate)]]];
                }
                if ($today) {
                    $planFilter[] = [
                        'plan.end_time' => ['BETWEEN', [strtotime(date("Y-m-d 00:00:00")), strtotime(date("Y-m-d 23:59:59"))]],
                    ];
                    $rFilter[] = ['report_month' => strtotime(date("Y-m") . '-01 00:00:00')];
                }
                $projectsReportList = model(ProjectCapacityReportModel::class)
                    ->where($rFilter)
                    ->select();
                $projectsReportList = array_group_by($projectsReportList, 'project_id');
                foreach ($projectsReportList as &$projectsReportListItem) {
                    $projectsReportListItem = array_column($projectsReportList, 'number', 'number_logic');
                }

                $planCount = model(PlanModel::class)
                    ->join('task_member on task_member.task_id = plan.task_id')
                    ->where($planFilter)
                    ->group('plan.type')
                    ->field("concat(plan.type,'_number') as type,count(distinct plan.id) as count")
                    ->select();
                $planCount = array_column($planCount, 'count', 'type');

                foreach (['review_number', 'meeting_number'] as $k) {
                    $currentReport = 0;
                    foreach ($projectIdList as $projectId) {
                        // 上报数据
                        $reportList = $projectsReportList[$projectId] ?? [];
                        // 导演数据
                        $directorUserList = $projectsDirectorUserIdList[$projectId] ?? [];
                        // 产能配置数据
                        $pcTemplate = $projectsPcTemplateList[$projectId] ?? [];
                        $directorNumber = count($directorUserList);
                        if (isset($reportList[$k])) {
                            $currentReport += $reportList[$k] * $directorNumber;
                        } else if (isset($pcTemplate[$k])) {
                            $currentReport += $pcTemplate[$k] * $directorNumber;
                        } else {
                            $currentReport += 0;
                        }
                    }

                    $res['report'][$k] = $currentReport;
                    if (isset($planCount[$k])) {
                        $res['plan'][$k] = $planCount[$k];
                    } else {
                        $res['plan'][$k] = 0;
                    }
                }

                break;
            case 'storyboard':
            case 'sequence':
            case 'episode':
            default:
                $pFilter = [
                    'p.end_time' => ['BETWEEN', [$startDate, $endDate]],
                    'p.project_id' => ['IN', $projectIdList],
                    'p.type' => PlanModel::TYPE_STEP_CATEGORY,
                    'p.scope' => PlanModel::SCOPE_SINGLE,
                    'trt.link_id' => ['IN', $stepCategoryIdList],
                ];
                $episodeList = model(PlanModel::class)->alias('p')
                    ->join('task on task.id = p.task_id')
                    ->join("(select * from task_relation_tag where link_module_code in ('step_category', 'show_step_category')) trt on trt.task_id = task.id")
                    ->field('p.entity_id')
                    ->where($pFilter)
                    ->group('p.entity_id')
                    ->select();
                $reportCount = 0;
                foreach ($projectIdList as $projectId) {
                    $realReport = $projectsRealReportList[$projectId] ?? [];
                    $realReport = array_column($realReport, 'number', 'number_logic');
                    $pcTemplate = $projectsPcTemplateList[$projectId] ?? [];
                    $pcTemplate = array_column($pcTemplate, 'number', 'number_logic');
                    $reportCount += $realReport['episode'] ?? $pcTemplate['episode'] ?? 0;
                }
                $res['report']['episode'] = $reportCount;
                $res['plan']['episode'] = count($episodeList);

        }

        return $res;

    }

    /**
     * 获得分类module_code
     * @param array $stepCategoryIdList
     * @return mixed|string
     */
    public function getStepCategoryListModuleCode(array $stepCategoryIdList)
    {
        $stepCategoryIds = implode(',', $stepCategoryIdList);
        $topLevelStepCategoryList = model(StepCategoryModel::class)
            ->where("id in (select parent_id from step_category where id in ({$stepCategoryIds}))")
            ->select();

        if (!empty($topLevelStepCategoryList)) {
            $moduleIdList = array_column($topLevelStepCategoryList, 'entity_id');
            ProjectCapacityReportService::getInstance()->validatePCModuleList($moduleIdList);
            $moduleCode = module_by_id($moduleIdList[0])['code'];
        } else {
            $moduleCode = TaskService::DIRECTOR_CODE;
        }
        return $moduleCode;
    }

    /**
     * 月度产能任务列表
     * @param        $projectIdList
     * @param array  $stepCategoryIdList
     * @param string $month
     * @param string $categoryName
     * @param int    $directorUserId
     * @param bool   $today
     * @param        $filter
     * @param        $page
     * @return array|false|mixed|string
     */
    public function monthReportTaskList($projectIdList, array $stepCategoryIdList, string $month, string $categoryName, int $directorUserId, bool $today, $filter, $page)
    {
        $res = [];

        $moduleCode = $this->getStepCategoryListModuleCode($stepCategoryIdList);

        $monthFilter = [];
        if (!empty($month)) {
            $startDate = strtotime($month . '-01 00:00:00');
            $endDate = strtotime(" +1month-1seconds", $startDate);
            $monthFilter = [
                [
                    'p.end_time' => ['BETWEEN', [$startDate, $endDate]],
                ]
            ];
        }


        $pFilter = [
            'p.project_id' => ['IN', $projectIdList],
            'p.type' => PlanModel::TYPE_STEP_CATEGORY,
            'p.scope' => PlanModel::SCOPE_SINGLE,
            'trt.link_id' => ['IN', $stepCategoryIdList],
        ];

        if (!empty($monthFilter)) {
            $pFilter[] = $monthFilter;
        }

        $episodeStepCategoryPlanList = model(PlanModel::class)->alias('p')
            ->join('task on task.id = p.task_id')
            ->join("(select * from task_relation_tag where link_module_code in ('step_category', 'show_step_category')) trt on trt.task_id = task.id")
            ->field('p.start_time,p.end_time,p.entity_id as episode_id,trt.link_id as step_category_id')
            ->where($pFilter)
            ->group('p.entity_id,trt.link_id')
            ->select();
        $taskCountEpisodeStepCategoryFilter = [];
        $episodeIdList = [];
        foreach ($episodeStepCategoryPlanList as $episodeStepCategoryPlanItem) {
            $taskCountEpisodeStepCategoryFilter[] = [
                'task.episode_id' => $episodeStepCategoryPlanItem['episode_id'],
                'task.step_category_id' => $episodeStepCategoryPlanItem['step_category_id'],
            ];
            $episodeIdList[$episodeStepCategoryPlanItem['episode_id']] = $episodeStepCategoryPlanItem['episode_id'];
        }
        $projectList = model(ProjectModel::class)->where(['id' => ['IN', $projectIdList]])->field('id,name')->select();
        $projectList = array_column($projectList, null, 'id');
        switch ($moduleCode) {
            case 'design':
            case 'asset':
            case 'level':
                if (empty($taskCountEpisodeStepCategoryFilter)) {
                    return $res;
                }
                $qf = [
                    'task.id',
                    'task.step_type',
                    'task.project_id',
                    'task.name',
                    'task.episode_id',
                    'task.json',
                    'task.plan_end_time',
                    'task.end_time',
                    'entity.id',
                    'entity.module_id',
                    'entity.project_id',
                    'entity.name',
                    'entity.code',
                    'entity.initial_episode_code',
                    'entity.showings_number',
                    'entity.json',
                ];
                $taskCountEpisodeStepCategoryFilter['_logic'] = 'or';
                $taskCountFilter = [
                    $taskCountEpisodeStepCategoryFilter,
                    'task.step_type' => 'step_category',
                    'task.project_id' => ['IN', $projectIdList],
                    "JSON_UNQUOTE(JSON_EXTRACT(entity.json,concat('$.',module.code,'_category')))" => $categoryName,
                ];
                $filter = $this->parseMultipleEntityTaskFilter($filter);
                $res = model(TaskModel::class)
                    ->join('entity on entity.id = task.entity_id')
                    ->join('module on module.id = entity.module_id')
                    ->where($taskCountFilter)
                    ->where($filter)
                    ->page($page[0], $page[1])
                    ->field(build_complex_query_fields($qf, '__'))
                    ->select();
                $taskStatusIdList = [];
                foreach ($res as &$row) {
                    $row = separate_complex_query_data($row, '__');
                    $taskJson = json_decode($row['task']['json'], true);
                    $entityJson = json_decode($row['entity']['json'], true);
                    $currentEntityModuleCode = module_by_id($row['entity']['module_id'], false)['code'] ?? '';
                    $upstreamEntityId = 0;
                    if ($currentEntityModuleCode == 'design') {
                        $upstreamEntityId = $row['entity']['id'];
                    } else {
                        $upstreamEntityId = $entityJson[$currentEntityModuleCode . '_design'] ?? 0;
                    }
                    $row['entity']['upstream_entity_id'] = $upstreamEntityId;
                    foreach ($entityJson as $jk => $jv) {
                        $jk = str_replace($currentEntityModuleCode . '_', '', $jk);
                        $row['entity'][$jk] = $jv;
                    }
                    $row['task_status'] = $taskJson['task_status'] ?? null;
                    if (!empty($row['task_status']) && !in_array($row['task_status'], $taskStatusIdList)) {
                        $taskStatusIdList[] = $row['task_status'];
                    }

                    unset($row['entity']['json'], $row['task']['json']);
                }

                $taskStatusList = [];
                if (!empty($taskStatusIdList)) {
                    $taskStatusList = StatusService::getInstance()->getList(['id' => ['IN', $taskStatusIdList]], 'id,name,code');
                    $taskStatusList = array_column($taskStatusList, null, 'id');
                }
                foreach ($res as &$row) {
                    if (!empty($row['task_status']) && isset($taskStatusList[$row['task_status']])) {
                        $row['task_status'] = $taskStatusList[$row['task_status']];
                    }
                    $row['project'] = $projectList[$row['task']['project_id']] ?? null;
                }
                break;
            case 'shot':
                $firstStepCategoryId = $stepCategoryIdList[0];
                $firstStepCategory = model(StepCategoryModel::class)->find($firstStepCategoryId);
                // 镜头解算 任务列表
                if (empty($episodeIdList)) {
                    return $res;
                }
                if (isset($filter['task_name'])) {
                    $filter['entity.name'] = $filter['task_name'];
                    unset($filter['task_name']);
                }
                $res = $this->getEpisodeList($episodeIdList, $filter, $page);
                $this->appendEpisodeAssignData($projectIdList, $res, $stepCategoryIdList);
                if (empty($res)) {
                    return $res;
                }
                $episodeCountFilter = [];

                foreach ($res as $episode) {
                    $episodeCountFilter[] = [
                        'entity.initial_episode_code' => $episode['entity']['code'],
                        'entity.project_id' => $episode['project']['id'],
                    ];
                }

                switch ($firstStepCategory['code']) {
                    case 'efx':
                        $efxCount = model(EntityModel::class)
                            ->where(['entity.module_id' => module_by_code('shot')['id']])
                            ->where($episodeCountFilter)
                            ->field("
                            count(json ->> '$.shot_fx' in ('混合特效', '其他特效', '引擎特效') or null) efx_num,
                            count(entity.id) total,
                            concat(entity.project_id, entity.initial_episode_code) map_key")
                            ->group('entity.project_id,entity.initial_episode_code')
                            ->select();
                        $efxCount = array_column($efxCount, null, 'map_key');
                        foreach ($res as &$episode) {
                            $mapKey = $episode['project']['id'] . $episode['entity']['code'];
                            if (isset($efxCount[$mapKey])) {
                                $episode['shot_number'] = $efxCount[$mapKey]['total'];
                                $episode['efx_shot_number'] = $efxCount[$mapKey]['efx_num'];
                            } else {
                                $episode['shot_number'] = 0;
                                $episode['efx_shot_number'] = 0;
                            }
                        }
                        break;
                    case 'lighting':
                    case 'shot_solution':
                        $episodeCountFilter['_logic'] = 'OR';
                        $episodeCount = model(EntityModel::class)
                            ->where(['entity.module_id' => module_by_code('shot')['id']])
                            ->where($episodeCountFilter)
                            ->field("count(entity.json ->> '$.shot_cloth_solution' in ('布料毛发解算', '布料解算', '毛发解算') or null) as need_jiesuan,
       count(entity.id) as total, concat(entity.project_id,entity.initial_episode_code) as map_key")
                            ->group('entity.project_id,entity.initial_episode_code')
                            ->select();
                        $episodeCount = array_column($episodeCount, null, 'map_key');
                        foreach ($res as &$episode) {
                            $mapKey = $episode['project']['id'] . $episode['entity']['code'];
                            if (isset($episodeCount[$mapKey])) {
                                $episode['shot_number'] = $episodeCount[$mapKey]['total'];
                                $episode['jiesuan_shot_number'] = $episodeCount[$mapKey]['need_jiesuan'];
                            } else {
                                $episode['shot_number'] = 0;
                                $episode['jiesuan_shot_number'] = 0;
                            }
                        }
                        break;
                    default:
                }
                break;
            case TaskService::DIRECTOR_CODE:
                $role = teamones_request('teamones-im', 'role/find', ['param' => ['filter' => ['code' => TaskService::DIRECTOR_CODE]]])['data'];
                if (empty($role)) {
                    return $res;
                }
                $userFilter = [
                    'project_id' => ['IN', $projectIdList],
                    'role_id' => $role['id'],
                ];

                if (!empty($directorUserId)) {
                    $userFilter['user_id'] = $directorUserId;
                }

                $userIdList = model(ProjectMemberModel::class)
                    ->join('user on user.id = project_member.user_id')
                    ->where($userFilter)
                    ->group('user.id')
                    ->field('user.id')
                    ->select();

                if (empty($userIdList)) {
                    return $res;
                }
                $userIdList = array_column($userIdList, 'id');

                $planFilter = [
                    'p.project_id' => ['IN', $projectIdList],
                    'p.type' => ["IN", [PlanModel::TYPE_MEETING, PlanModel::TYPE_REVIEW]],
                    'task_member.partner' => ["IN", $userIdList]
                ];

                if (!empty($monthFilter)) {
                    $planFilter[] = $monthFilter;
                }
                if ($today) {
                    $planFilter[] = [
                        'p.end_time' => ['BETWEEN', [strtotime(date("Y-m-d 00:00:00")), strtotime(date("Y-m-d 23:59:59"))]],
                    ];
                }
                if (!empty($filter)) {
                    $planFilter[] = $filter;
                }

                $res = model(PlanModel::class)
                    ->alias('p')
                    ->join('task on task.id = p.task_id')
                    ->join('entity on entity.id = task.entity_id', 'LEFT')
                    ->join('task_member on task_member.task_id = task.id')
                    ->where($planFilter)
                    ->page($page[0], $page[1])
//                    ->field("task.id,task.name, task_member.status,task_member.partner,p.start_time,p.end_time")
                    ->group('p.id')
                    ->field('p.id')
                    ->select();
                if (empty($res)) {
                    return $res;
                }

                $pidList = array_column($res, 'id');
                $taskListFilter = [
                    'p.id' => ['IN', $pidList]
                ];
                $res = model(PlanModel::class)
                    ->alias('p')
                    ->join('task on task.id = p.task_id')
                    ->where($taskListFilter)
                    ->field("task.id,task.project_id,task.name,task.json->>'$.task_status' status,p.start_time,p.end_time")
                    ->select();

                $userIds = [];
                $statusIds = [];
                $taskIdList = [];

                foreach ($res as $resItem) {
                    $taskIdList[] = $resItem['id'];
                    if (!empty($resItem['status'])) {
                        $statusIds[$resItem['status']] = $resItem['status'];
                    }
                }
                $taskMemberList = [];
                if (!empty($taskIdList)) {
                    $taskMemberList = model(TaskMemberModel::class)->where(['task_id' => ['IN', $taskIdList]])->select();
                }

                foreach ($taskMemberList as $taskMember) {
                    $userIds[$taskMember['user_id']] = $taskMember['user_id'];
                    $userIds[$taskMember['partner']] = $taskMember['partner'];
                }
                unset($userIds[0]);
                $taskMemberList = array_group_by($taskMemberList, 'task_id');
                $userIds = array_values($userIds);
                $statusIds = array_values($statusIds);

                $statusList = [];
                if (!empty($statusIds)) {
                    $statusList = model(StatusModel::class)->where(['id' => ['IN', $statusIds]])->field('id,name,code,color,icon')->select();
                    $statusList = array_column($statusList, null, 'id');
                }

                $userList = model(UserModel::class)->where(['id' => ["IN", $userIds]])->field('id,name,avatar')->select();
                $userList = array_column($userList, null, 'id');

                $res = array_map(function ($resItem) use ($taskMemberList, $userList, $statusList, $projectList) {
                    $resItem['task_member_list'] = $taskMemberList[$resItem['id']] ?? [];
                    foreach ($resItem['task_member_list'] as &$taskMember) {
                        if ($taskMember['partner'] > 0) {
                            $taskMember['user_info'] = $userList[$taskMember['partner']] ?? null;
                        }
                    }
                    $resItem['status'] = $statusList[$resItem['status']] ?? null;
                    $resItem['project'] = $projectList[$resItem['project_id']] ?? null;
                    return $resItem;
                }, $res);
                break;
            case 'episode':
                // 剧本
            case 'storyboard':
                // 分镜
            case 'sequence':
                // 序列
            default:
                if (isset($filter['task_name'])) {
                    $filter['entity.name'] = $filter['task_name'];
                    unset($filter['task_name']);
                }
                $res = $this->getEpisodeList($episodeIdList, $filter, $page);
                $this->appendEpisodeAssignData($projectIdList, $res, $stepCategoryIdList);
        }
        return $res;
    }

    /**
     * 月度产能上报情况 用户列表
     * @param int    $projectIdList
     * @param array  $stepCategoryIdList
     * @param string $month
     * @param string $categoryName
     * @param int    $directorUserId
     * @param bool   $today
     * @param        $filter
     * @param        $page
     * @return array|false|mixed|string
     */
    public function monthReportUserList($projectIdList, array $stepCategoryIdList, string $month, string $categoryName, int $directorUserId, bool $today, $filter, $page)
    {
        $res = [];
        $moduleCode = $this->getStepCategoryListModuleCode($stepCategoryIdList);

        $filter['plan_start_time'] = strtotime($month . '-01 00:00:00');
        $filter['plan_end_time'] = strtotime(" +1month-1seconds", $filter['plan_start_time']);
        $monthFilter = [
            [
                'p.end_time' => ['BETWEEN', [$filter['plan_start_time'], $filter['plan_end_time']]],
            ]
        ];


        $pFilter = [
            'p.project_id' => ['IN', $projectIdList],
            'p.type' => PlanModel::TYPE_STEP_CATEGORY,
            'p.scope' => PlanModel::SCOPE_SINGLE,
            'trt.link_id' => ['IN', $stepCategoryIdList],
        ];

        if ($moduleCode === TaskService::DIRECTOR_CODE) {
            unset($pFilter['trt.link_id']);
        }

        if (!empty($monthFilter)) {
            $pFilter[] = $monthFilter;
        }

        $episodeStepCategoryPlanList = model(PlanModel::class)->alias('p')
            ->join('task on task.id = p.task_id')
            ->join("(select * from task_relation_tag where link_module_code in ('step_category', 'show_step_category')) trt on trt.task_id = task.id", 'LEFT')
            ->field('p.start_time,p.end_time,p.entity_id as episode_id,trt.link_id as step_category_id')
            ->where($pFilter)
            ->group('p.entity_id,trt.link_id')
            ->select();

        $taskCountEpisodeStepCategoryFilter = [];
        $todoTaskCountEpisodeStepCategoryFilter = [];
        $episodeIdList = [];
        foreach ($episodeStepCategoryPlanList as $episodeStepCategoryPlanItem) {
            $taskCountEpisodeStepCategoryFilter[] = [
                'task.episode_id' => $episodeStepCategoryPlanItem['episode_id'],
                'task.step_category_id' => $episodeStepCategoryPlanItem['step_category_id'],
            ];
            $todoTaskCountEpisodeStepCategoryFilter[] = [
                'r_episode.link_id' => $episodeStepCategoryPlanItem['episode_id'],
                [
                    'r_step_category.link_id' => $episodeStepCategoryPlanItem['step_category_id'],
                    'plan.step_category_id' => $episodeStepCategoryPlanItem['step_category_id'],
                    '_logic' => 'OR',
                ]
            ];
            $episodeIdList[$episodeStepCategoryPlanItem['episode_id']] = $episodeStepCategoryPlanItem['episode_id'];
        }
        if (empty($taskCountEpisodeStepCategoryFilter)) {
            return $res;
        }
        $taskCountEpisodeStepCategoryFilter['_logic'] = 'OR';
        $todoTaskCountEpisodeStepCategoryFilter['_logic'] = 'OR';

        switch ($moduleCode) {
            case 'design':
            case 'asset':
            case 'level':
                unset($filter['plan_start_time'], $filter['plan_end_time']);
                $roleFilter = [
                    'param' => [
                        'filter' => [
                            'role.code' => ['-in', TaskService::HEADMAN_CODE . ',' . TaskService::ARTILIST_CODE]
                        ],
                    ],
                ];
                $roleList = teamones_request('teamones-im', 'role/select', $roleFilter);
                $roleIdList = array_column($roleList['data']['rows'], 'id');
                $pmFilter = [
                    'project_member.project_id' => ["IN", $projectIdList],
                    'project_member.role_id' => ['IN', $roleIdList],
                ];

                $pmStepCategoryFilter = [];
                foreach ($stepCategoryIdList as $stepCategoryId) {
                    $pmStepCategoryFilter[] = ['project_member.step_category_ids' => ['FIND_IN_SET', $stepCategoryId]];
                }
                $pmStepCategoryFilter['_logic'] = 'or';
                $pmFilter[] = [$pmStepCategoryFilter];

                if (!empty($filter)) {
                    $pmFilter[] = $filter;
                }
                $userIdList = model(ProjectMemberModel::class)
                    ->join('user on user.id = project_member.user_id')
                    ->where($pmFilter)
                    ->group('user.id')
                    ->field('user.id')
                    ->page($page[0], $page[1])
                    ->select();
                if (empty($userIdList)) {
                    return $res;
                }

                $res = model(UserModel::class)
                    ->where(['id' => ['IN', array_column($userIdList, 'id')]])
                    ->field('id,name,avatar')
                    ->select();
                $closedStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();
                $qf = [
                    'task.executor',
                    'count(task.id) as total',
                    "count(task.json->>'$.task_status' = 7 or null) as done",
                ];
                $taskCountFilter = [
                    'task.step_type' => 'step',
                    'task.project_id' => ["IN", $projectIdList],
                    "JSON_UNQUOTE(JSON_EXTRACT(entity.json,concat('$.',module.code,'_category')))" => $categoryName,
                    'task.step_category_id' => ['IN', $stepCategoryIdList],
                    "task.json->>'$.task_status'" => ['NEQ', $closedStatus['id']],
                    'task.executor' => ['IN', array_column($userIdList, 'id')],
                ];
                $taskCountFilter[] = $taskCountEpisodeStepCategoryFilter;

                $userCount = model(TaskModel::class)
                    ->join('entity on entity.id = task.entity_id')
                    ->join('module on module.id = entity.module_id')
                    ->where($taskCountFilter)
                    ->field($qf)
                    ->select();

                $userCount = array_column($userCount, null, 'executor');
                foreach ($res as &$row) {
                    $row['total'] = isset($userCount[$row['id']]) ? $userCount[$row['id']]['total'] : 0;
                    $row['done'] = isset($userCount[$row['id']]) ? $userCount[$row['id']]['done'] : 0;
                }

                break;
            case 'shot':
                unset($filter['plan_start_time'], $filter['plan_end_time']);
                $roleFilter = [
                    'param' => [
                        'filter' => [
                            'role.code' => ['-in', TaskService::HEADMAN_CODE . ',' . TaskService::ARTILIST_CODE]
                        ]
                    ],
                ];
                $roleList = teamones_request('teamones-im', 'role/select', $roleFilter);
                $roleIdList = array_column($roleList['data']['rows'], 'id');
                $pmFilter = [
                    'project_member.project_id' => ["IN", $projectIdList],
                    'project_member.role_id' => ['IN', $roleIdList]
                ];
                $pmStepCategoryFilter = [];
                foreach ($stepCategoryIdList as $stepCategoryId) {
                    $pmStepCategoryFilter[] = ['project_member.step_category_ids' => ['FIND_IN_SET', $stepCategoryId]];
                }
                $pmStepCategoryFilter['_logic'] = 'or';
                $pmFilter[] = $pmStepCategoryFilter;
                if (!empty($filter)) {
                    $pmFilter[] = $filter;
                }
                $userIdList = model(ProjectMemberModel::class)
                    ->join('user on user.id = project_member.user_id')
                    ->where($pmFilter)
                    ->group('user.id')
                    ->field('user.id')
                    ->page($page[0], $page[1])
                    ->select();
                if (empty($userIdList)) {
                    return $res;
                }

                $res = model(UserModel::class)
                    ->where(['id' => ['IN', array_column($userIdList, 'id')]])
                    ->field('id,name,avatar')
                    ->select();
                $closedStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();
                $qf = [
                    'task.executor',
                    'count(task.id) as total',
                    "count(task.json->>'$.task_status' = 7 or null) as done",
                ];
                $taskCountFilter = [
                    'task.step_type' => 'step',
                    'task.project_id' => ["IN", $projectIdList],
                    'task.step_category_id' => ['IN', $stepCategoryIdList],
                    "task.json->>'$.task_status'" => ['NEQ', $closedStatus['id']],
                    'task.executor' => ['IN', array_column($userIdList, 'id')],
                ];
                $taskCountFilter[] = $taskCountEpisodeStepCategoryFilter;

                $userCount = model(TaskModel::class)
                    ->join('entity on entity.id = task.entity_id')
                    ->join('module on module.id = entity.module_id')
                    ->where($taskCountFilter)
                    ->field($qf)
                    ->select();

                $userCount = array_column($userCount, null, 'executor');
                foreach ($res as &$row) {
                    $row['total'] = isset($userCount[$row['id']]) ? $userCount[$row['id']]['total'] : 0;
                    $row['done'] = isset($userCount[$row['id']]) ? $userCount[$row['id']]['done'] : 0;
                }
                break;
            case TaskService::DIRECTOR_CODE:
                $planStartFilter = $filter['plan_start_time'] ?? 0;
                $planEndFilter = $filter['plan_end_time'] ?? 0;
                unset($filter['plan_start_time'], $filter['plan_end_time']);
                $role = teamones_request('teamones-im', 'role/find', ['param' => ['filter' => ['code' => TaskService::DIRECTOR_CODE]]])['data'];
                if (empty($role)) {
                    return $res;
                }

                $planWhere = [
                    'plan.project_id' => ["IN", $projectIdList],
                    'plan.type' => ['IN', [PlanModel::TYPE_REVIEW, PlanModel::TYPE_MEETING]],
                ];
                if ($planStartFilter > 0 && $planEndFilter > 0) {
                    $planWhere['plan.end_time'] = ['BETWEEN', [$planStartFilter, $planEndFilter]];
                }
                if ($today) {
                    $planWhere[] = ['plan.end_time' => ['BETWEEN', [strtotime(date("Y-m-d 00:00:00")), strtotime(date("Y-m-d 23:59:59"))]],];
                }
                $userWhere = $planWhere;
                $userWhere[] = [
                    'project_member.role_id' => $role['id'],
                    'project_member.project_id' => ["IN", $projectIdList],
                ];
                if (!empty($filter)) {
                    $userWhere[] = $filter;
                }
                if ($directorUserId > 0) {
                    $userWhere['user.id'] = $directorUserId;
                }
                $userIdList = model(PlanModel::class)
                    ->join('task_member on task_member.task_id = plan.task_id')
                    ->join('task on task_member.task_id = task.id')
                    ->join('project_member on project_member.user_id = task_member.partner')
                    ->join('user on project_member.user_id = user.id')
                    ->where($userWhere)
                    ->field('user.id')
                    ->group('user.id')
                    ->page($page[0], $page[1])
                    ->select();
                if (empty($userIdList)) {
                    return $res;
                }
                $userIdList = array_column($userIdList, 'id');
                $planWhere['task_member.partner'] = ['IN', $userIdList];
                $res = model(UserModel::class)->where(['id' => ['IN', $userIdList]])->field('id,name,avatar')->select();
                $userTaskCount = model(PlanModel::class)
                    ->join('task_member on task_member.task_id = plan.task_id')
                    ->join('task on task_member.task_id = task.id')
                    ->join('user on task_member.partner = user.id')
                    ->where($planWhere)
                    ->field('task_member.partner,count(plan.task_id) as total,count(task_member.status = 7 or null) as done')
                    ->group('task_member.partner')
                    ->select();
                $userTaskCount = array_column($userTaskCount, null, 'partner');

                foreach ($res as &$user) {
                    $user['total'] = 0;
                    $user['done'] = 0;
                    if (isset($userTaskCount[$user['id']])) {
                        $user['total'] = $userTaskCount[$user['id']]['total'];
                        $user['done'] = $userTaskCount[$user['id']]['done'];
                    }
                }
                break;
            case 'episode':
                // 剧本
            case 'storyboard':
                // 分镜
            case 'sequence':
                // 序列
            default:
                unset($filter['plan_start_time'], $filter['plan_end_time']);
                $pmFilter = [
                    'project_member.project_id' => ["IN", $projectIdList],
                ];
                $pmStepCategoryFilter = [];
                foreach ($stepCategoryIdList as $stepCategoryId) {
                    $pmStepCategoryFilter[] = ['project_member.step_category_ids' => ['FIND_IN_SET', $stepCategoryId]];
                }
                $pmStepCategoryFilter['_logic'] = 'or';
                $pmFilter[] = $pmStepCategoryFilter;
                if (!empty($filter)) {
                    $pmFilter[] = $filter;
                }
                $userIdList = model(ProjectMemberModel::class)
                    ->join('user on user.id = project_member.user_id')
                    ->where($pmFilter)
                    ->group('user.id')
                    ->field('user.id')
                    ->page($page[0], $page[1])
                    ->select();
                if (empty($userIdList)) {
                    return $res;
                }

                $res = model(UserModel::class)
                    ->where(['id' => ['IN', array_column($userIdList, 'id')]])
                    ->field('id,name,avatar')
                    ->select();
                $closedStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();
                $qf = [
                    'task_member.user_id',
                    'count(distinct plan.task_id) as total',
                    "count(distinct plan.task_id and task_member.status = 7 or null) as done",
                ];
                $taskCountFilter = [
//                    'plan.type' => ['IN', PlanModel::TYPE_TODO_GROUP],
                    'plan.project_id' => ["IN", $projectIdList],
                    "task.json->>'$.task_status'" => ['NEQ', $closedStatus['id']],
                    'task_member.user_id' => ['IN', array_column($userIdList, 'id')],
                ];
                $taskCountFilter[] = $todoTaskCountEpisodeStepCategoryFilter;

                $userCount = model(PlanModel::class)
                    ->join('task on task.id = plan.task_id')
                    ->join('task_member on task_member.task_id = plan.task_id')
                    ->join("(select * from task_relation_tag where link_module_code ='episode') r_episode on r_episode.task_id = plan.task_id")
                    ->join("(select * from task_relation_tag where link_module_code in ('step_category', 'show_step_category')) r_step_category on r_step_category.task_id = plan.task_id", 'LEFT')
                    ->where($taskCountFilter)
                    ->field($qf)
                    ->select();

                $userCount = array_column($userCount, null, 'user_id');
                foreach ($res as &$row) {
                    $row['total'] = isset($userCount[$row['id']]) ? $userCount[$row['id']]['total'] : 0;
                    $row['done'] = isset($userCount[$row['id']]) ? $userCount[$row['id']]['done'] : 0;
                }
        }

        return $res;
    }

    /**
     * 查询剧集列表
     * @param $episodeIdList
     * @param $filter
     * @param $page
     * @return array|false|mixed|string
     */
    public function getEpisodeList($episodeIdList, $filter, $page)
    {
        $qf = [
            'entity.id',
            'entity.name',
            'entity.code',
            'entity.json',
            'project.id',
            'project.code',
            'project.name',
        ];
        $res = model(EntityModel::class)
            ->join('project on project.id = entity.project_id')
            ->field(build_complex_query_fields($qf, '__'))
            ->where(['entity.id' => ['IN', implode(',', $episodeIdList)]])
            ->where($filter)
            ->page($page[0], $page[1])
            ->select();
        if (empty($res)) {
            return $res;
        }
        $statusIdList = [];
        foreach ($res as &$episode) {
            $episode = separate_complex_query_data($episode, '__');
            $episodeJson = json_decode($episode['entity']['json'], true);
            unset($episode['entity']['json']);
            $episode['status'] = null;
            if (!empty($episodeJson['episode_status'])) {
                $statusIdList[$episodeJson['episode_status']] = $episodeJson['episode_status'];
                $episode['status'] = $episodeJson['episode_status'];
            }

        }
        $statusList = StatusService::getInstance()->getList(['id' => ['IN', implode(',', $statusIdList)]], 'id,name,code,color');
        $statusList = array_column($statusList, null, 'id');

        foreach ($res as &$episode) {
            if (isset($statusList[$episode['status']])) {
                $episode['status'] = $statusList[$episode['status']];
            }
        }

        return $res;
    }

    /**
     * 用户任务列表
     * @param array $param
     * @param       $projectIdList
     * @param       $month
     * @param       $stepCategoryIdList
     * @param       $userId
     * @return array
     * @throws Exception
     */
    public function userTaskList(array $param, $projectIdList, $month, $stepCategoryIdList, $userId)
    {
        $monthFilter = [];
        if (!empty($month)) {
            $startDate = strtotime($month . '-01 00:00:00');
            $endDate = strtotime(" +1month-1seconds", $startDate);
            $monthFilter = [
                [
                    'p.end_time' => ['BETWEEN', [$startDate, $endDate]],
                ]
            ];
        }

        $stepCategoryIdList = array_filter($stepCategoryIdList);
        $extraFilter = [];

        if (!empty($stepCategoryIdList)) {
            $pFilter = [
                'p.project_id' => ["IN", $projectIdList],
                'p.type' => PlanModel::TYPE_STEP_CATEGORY,
                'p.scope' => PlanModel::SCOPE_SINGLE,
                'trt.link_id' => ['IN', $stepCategoryIdList],
            ];
            if (!empty($monthFilter)) {
                $pFilter[] = $monthFilter;
            }
            $episodeStepCategoryPlanList = model(PlanModel::class)->alias('p')
                ->join('task on task.id = p.task_id')
                ->join("(select * from task_relation_tag where link_module_code in ('step_category', 'show_step_category')) trt on trt.task_id = task.id")
                ->field('p.start_time,p.end_time,p.entity_id as episode_id,trt.link_id as step_category_id')
                ->where($pFilter)
                ->group('p.entity_id,trt.link_id')
                ->select();
            if (empty($episodeStepCategoryPlanList)) {
                return [];
            }
            $todoTaskCountEpisodeStepCategoryFilter = [];
            foreach ($episodeStepCategoryPlanList as $episodeStepCategoryPlanItem) {
                $todoTaskCountEpisodeStepCategoryFilter[] = [
                    [
                        'r_episode.link_id' => $episodeStepCategoryPlanItem['episode_id'],
                        'task.episode_id' => $episodeStepCategoryPlanItem['episode_id'],
                        '_logic' => 'OR'
                    ],
                    [
                        'r_step_category.link_id' => $episodeStepCategoryPlanItem['step_category_id'],
                        'task.step_category_id' => $episodeStepCategoryPlanItem['step_category_id'],
                        '_logic' => 'OR',
                    ]
                ];
            }
            if (count($todoTaskCountEpisodeStepCategoryFilter) > 1) {
                $todoTaskCountEpisodeStepCategoryFilter['_logic'] = 'OR';
                $extraFilter = [$todoTaskCountEpisodeStepCategoryFilter];
            } else {
                $extraFilter = $todoTaskCountEpisodeStepCategoryFilter[0];
            }
        } else {
            // 环节id筛选为空 直接筛选任务排期
            unset($param['filter']['category_name']);
            if (!empty($month)) {
                $startDate = strtotime($month . '-01 00:00:00');
                $endDate = strtotime(" +1month-1seconds", $startDate);
                $extraFilter = [
                    'plan.end_time' => ['BETWEEN', [$startDate, $endDate]],
                ];
            }
            $param['no_step_category_id'] = true;
        }
        // todo 和olap同步实现分类的筛选
        unset($param['filter']['category_name']);
        $param['filter']['task.project_id'] = ["IN", $projectIdList];
        $param['filter'][] = $extraFilter;
        return ProducerManageService::getInstance()->taskList($param, 0, [], false);
    }

    /**
     * 查询月度产能上报记录
     * @param $projectIdList
     * @param $month
     * @param $stepCategoryId
     * @return array|false|mixed|string
     */
    public function getMonthReport($projectIdList, $month, $stepCategoryId)
    {
        $rFilter = [
            'project_id' => ['IN', $projectIdList],
            'step_category_id' => $stepCategoryId,
            'report_month' => strtotime($month . '-01 00:00:00'),
        ];
        return model(ProjectCapacityReportModel::class)
            ->where($rFilter)
            ->select();
    }

    /**
     * 追加分配率
     * @param $projectIdList
     * @param $episodeList
     * @param $stepCategoryIdList
     * @return void
     */
    public function appendEpisodeAssignData($projectIdList, &$episodeList, $stepCategoryIdList)
    {
        if (empty($episodeList)) {
            return $episodeList;
        }
        $episodeIdList = [];
        foreach ($episodeList as $episode) {
            $episodeIdList[] = $episode['entity']['id'];
        }

        $ef = [
            'project_id' => ['IN', $projectIdList], 'episode_id' => ['IN', $episodeIdList],
            'step_type' => 'step',
        ];
        $stepCategoryIdList = array_filter($stepCategoryIdList);
        if (count($stepCategoryIdList) >= 1) {
            $ef['task.step_category_id'] = ['IN', $stepCategoryIdList];
        }
        $episodeAssignCount = model(TaskModel::class)
            ->where($ef)
            ->field("episode_id, count(task.id) as total, count(task.assign_status = 'executor' or null) as assign_count")
            ->group("episode_id")
            ->select();

        $episodeAssignCount = array_column($episodeAssignCount, null, 'episode_id');
        foreach ($episodeList as &$episode) {
            $episodeCount = $episodeAssignCount[$episode['entity']['id']] ?? null;
            $episode['total_task'] = 0;
            $episode['assign_count'] = 0;

            if (!empty($episodeCount)) {
                $episode['total_task'] = $episodeCount['total'];
                $episode['assign_count'] = $episodeCount['assign_count'];

            }

            $episode['task_assign_p'] = 0;
            if ($episode['total_task'] > 0) {
                $episode['task_assign_p'] = floor(($episode['assign_count'] / $episode['total_task']) * 100) / 100;
            }
        }
    }

    private function queryMonthPlanTaskCountData(&$res, $taskCountFilter, $episodeStepCategoryPlanList)
    {
        $episodeStepCategoryTaskCount = model(TaskModel::class)
            ->join('entity on entity.id = task.entity_id')
            ->join('module on module.id = entity.module_id')
            ->where($taskCountFilter)
            ->field("count(task.id) as count,
                       concat(task.episode_id, '-', task.step_category_id) as group_key")
            ->group("task.episode_id,task.step_category_id")
            ->select();
        $episodeStepCategoryTaskCount = array_column($episodeStepCategoryTaskCount, 'count', 'group_key');
        foreach ($res as $month => &$count) {
            $episodeStepCategoryPlanListCurrentMonth = $episodeStepCategoryPlanList[$month] ?? [];
            foreach ($episodeStepCategoryPlanListCurrentMonth as $episodeStepCategoryPlanListCurrentMonthItem) {
                if (isset($episodeStepCategoryTaskCount[$episodeStepCategoryPlanListCurrentMonthItem['unique_key']])) {
                    $count += $episodeStepCategoryTaskCount[$episodeStepCategoryPlanListCurrentMonthItem['unique_key']];
                }
            }
        }
    }

    /**
     * 查询实际任务计划完成的数据
     * @param       $projectIdList
     * @param array $stepCategoryIdList
     * @param int   $startDate
     * @param int   $endDate
     * @param       $userInfo
     * @return array
     */
    public function selectTaskPlanData($projectIdList, array $stepCategoryIdList, int $startDate, int $endDate, $userInfo)
    {
        $moduleCode = $this->getStepCategoryListModuleCode($stepCategoryIdList);
        $res = [];

        $startMonth = date('Y-m', $startDate);
        $endMonth = date('Y-m', $endDate);
        $currentMonth = $startMonth;
        do {
            $res[$currentMonth] = 0;
            $currentMonth = date("Y-m", strtotime($currentMonth . ' +1 month'));
        } while (strtotime($currentMonth) <= strtotime($endMonth));


        $filter = [
            ['task.plan_end_time' => ['ELT', $endDate],],
            ['task.plan_end_time' => ['EGT', $startDate],],
            'task.project_id' => ['IN', $projectIdList],
            'r_step_category.link_id' => ['IN', $stepCategoryIdList],
            'task.department_id' => $userInfo['department_id'],
            'task.executor' => $userInfo['user_id'],
        ];

        switch ($moduleCode) {
            case 'design':
            case 'asset':
            case 'level':
            case 'shot':
            case 'sequence':
            case 'storyboard':
            case 'episode':
            case TaskService::DIRECTOR_CODE:
                // 资产、概念、关卡、镜头、导演 都有任务直接统计任务计划完成数量
                $countData = model(TaskModel::class)
                    ->join("(select * from task_relation_tag where link_module_code in ('step_category','show_step_category')) r_step_category on r_step_category.task_id = task.id")
                    ->field("from_unixtime(task.plan_end_time, '%Y-%m') group_date,count(distinct task.id) as number")
                    ->group("from_unixtime(task.plan_end_time, '%Y-%m')")
                    ->where($filter)
                    ->select();
                $countData = array_column($countData, 'number', 'group_date');
                foreach ($res as $month => &$count) {
                    $count = $countData[$month] ?? 0;
                }
                break;
            default:
                // todo
        }
        return $res;
    }

    /**
     * 查询项目每月产能情况
     * @param array $projectIds
     * @param array $stepCategoryIdList
     * @param int   $startDate
     * @param int   $endDate
     * @return array
     */
    public function getProjectEntityNumberCountByMonth(array $projectIds, array $stepCategoryIdList, int $startDate, int $endDate)
    {
        $res = [];
        $startMonth = date('Y-m', $startDate);
        $endMonth = date('Y-m', $endDate);
        $currentMonth = $startMonth;

        do {
            //获取当月产能
            $numberList = ProjectCapacityReportMonthService::getInstance()->getMonthReportNumberByMonths($projectIds, $stepCategoryIdList[0], $currentMonth);
            $res[] = [
                'month' => $currentMonth,
                'product_capacity' => $numberList['product_capacity'],
                'product_capacity_out' => $numberList['product_capacity_out'],
                'requirement_num' => $numberList['requirement_num']
            ];

            $currentMonth = date("Y-m", strtotime($currentMonth . ' +1 month'));
        } while (strtotime($currentMonth) <= strtotime($endMonth));

        return $res;
    }

    /**
     * 产能月详情
     * @param $param
     * @return array|null
     */
    public function getProjectEntityCountMonthInfo($param)
    {
        $startDate = strtotime($param['start_date']);
        $endDate = strtotime('+1 month -1 seconds', strtotime($param['end_date']));

        $list = $this->getPlanEntityNumberList($param['project_id'], $param['step_category_id_list'], $startDate, $endDate);
        return $list;
    }

    /**
     * 查询计划完成的实体数量
     * @param array $projectIds         项目IDs
     * @param array $stepCategoryIdList 环节ID
     * @param int   $startDate          开始时间
     * @param int   $endDate            结束时间
     * @return array|void
     */
    private function getPlanEntityNumberList(array $projectIds, array $stepCategoryIdList, int $startDate, int $endDate)
    {
        $now = time();
        // 查询出指定时间内的计划---剧集
        $planEpisodeList = model(PlanModel::class)
            ->where([
                'plan.project_id' => ['IN', $projectIds],
                'plan.scope' => PlanModel::SCOPE_SINGLE,
                'plan.type' => PlanModel::TYPE_STEP_CATEGORY,
                'r_step_category.link_id' => ['IN', $stepCategoryIdList],
                'plan.end_time' => ['EGT', $startDate],
                'plan.start_time' => ['ELT', $endDate],
            ])
            ->join('entity on entity.id = plan.entity_id')
            ->join("task_relation_tag r_step_category ON r_step_category.task_id = plan.task_id and r_step_category.link_module_code in ('step_category', 'show_step_category')")
            ->group('plan.entity_id')
            ->field('entity.code,entity.id,plan.project_id,plan.start_time,plan.end_time')
            ->cache(C('database.default_query_cache_time'))
            ->select();

        //剧集完成 -- 查询已完成；  剧集未完--查询不是取消的任务
        $entityIdsEnd = [];
        $entityIds = [];
        foreach ($planEpisodeList as $val) {
            if ($val['end_time'] < $now) {
                $entityIdsEnd[] = $val['id'];
            } else {
                $entityIds[] = $val['id'];
            }
        }
        //获取 镜头数量
        $listOld = $this->getTaskEpisodeNumber($projectIds, $stepCategoryIdList, $entityIdsEnd, true);
        $listNew = $this->getTaskEpisodeNumber($projectIds, $stepCategoryIdList, $entityIds, false);

        $numList = array_merge($listOld, $listNew);
        $numList = array_column($numList, 'num', 'episode_id');
        //组装数据
        foreach ($planEpisodeList as &$pval) {
            $pval['need_number'] = $numList[$pval['id']] ?? 0;
        }
        return $planEpisodeList;
    }


    /**
     * 查询项目产能平均数
     * @param array $projectIds         项目IDs
     * @param array $stepCategoryIdList 环节IDs
     * @return array
     */
    public function getPlanEpisodeAvgNumber(array $projectIds, array $stepCategoryIdList)
    {
        $startDate = strtotime(date('Y-m', strtotime('-7 month')) . '-01');
        $endDate = strtotime(date('Y-m', strtotime('-1 month')) . '-01') - 1;

        // 查询出指定时间内的计划---剧集
        $planEpisodeList = model(PlanModel::class)
            ->where([
                'plan.project_id' => ['IN', $projectIds],
                'plan.scope' => PlanModel::SCOPE_SINGLE,
                'plan.type' => PlanModel::TYPE_STEP_CATEGORY,
                'r_step_category.link_id' => ['IN', $stepCategoryIdList],
                'plan.end_time' => ['EGT', $startDate],
                'plan.start_time' => ['ELT', $endDate],
            ])
            ->join('entity on entity.id = plan.entity_id')
            ->join("task_relation_tag r_step_category ON r_step_category.task_id = plan.task_id and r_step_category.link_module_code in ('step_category', 'show_step_category')")
            ->group('plan.entity_id')
            ->field('entity.code,entity.id,plan.project_id,plan.start_time,plan.end_time')
            ->cache(C('database.default_query_cache_time'))
            ->select();

        $entityIds = array_column($planEpisodeList, 'id');
        //获取 镜头平均数量
        $total = $this->getTaskEpisodeNumber($projectIds, $stepCategoryIdList, $entityIds, true, true);

        $rt = [];
        $rt['avg_need_number'] = 0;
        $rt['total_need_number'] = 0;
        $rt['day_num'] = 0;
        if ($total) {
            $dayNum = $this->getDayNum($startDate, $endDate);
            $rt['avg_need_number'] = intval($total / $dayNum * 30);
            $rt['total_need_number'] = $total;
            $rt['day_num'] = $dayNum;
        }
        return $rt;
    }

    /**
     * 获取天数（去除法定节日）
     * @param int $startDate 开始时间
     * @param int $endDate   结束时间
     * @return int
     */
    private function getDayNum(int $startDate, int $endDate)
    {
        $holidayNum = 0;
        try {
            //计算法定节日
            $holiday = new Holiday();
            $holidayNum = $holiday->count($startDate, $endDate, Holiday::TYPE_VACATION);
        } catch (\Exception $e) {
        }

        $dayNum = intval(($endDate - $startDate) / 86400);
        $dayNum = $dayNum - $holidayNum;

        return $dayNum;
    }


    /**
     * 获取 镜头数量
     * @param array $projectIds         项目ID
     * @param array $stepCategoryIdList 环节iD
     * @param array $entityIds          实体id
     * @param bool  $isEnd              是否完成
     * @return array | int
     */
    private function getTaskEpisodeNumber(array $projectIds, array $stepCategoryIdList, array $entityIds, bool $isEnd = false, $isTotal = false)
    {
        if (empty($entityIds)) {
            return [];
        }
        //是否查询已完成
        if ($isEnd) {
            $countWhere["task.json->>'$.task_status'"] = ['EQ', StatusService::getInstance()->getTaskDoneStatus()['id']];
        } else {
            $countWhere["task.json->>'$.task_status'"] = ['NEQ', StatusService::getInstance()->getTaskDefaultClosedStatus()['id']];
        }

        // 查询每集的镜头数量
        $taskCountList = model(TaskModel::class)
            ->where([
                'project_id' => ['IN', $projectIds],
                'step_type' => TaskModel::STEP_TYPE_STEP,
                'step_category_id' => ['IN', $stepCategoryIdList],
                'episode_id' => ['IN', $entityIds],
            ])
            ->where($countWhere)
            ->group('episode_id')
            ->field("episode_id, count(distinct entity_id) num")
            ->cache(C('database.default_query_cache_time'))
            ->select();

        //返回总数
        if ($isTotal) {
            return intval(array_sum(array_column($taskCountList, 'num')));
        }

        return $taskCountList;
    }

}
